/* free.c - a /proc implementation of free */
/* Dec14/92 by Brian Edmonds */
/* Thanks to Rafal Maszkowski for the Total line */

#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sysinfo.h>

/* example data, following junk, with comments added:
 *
 * MemTotal:        61768 kB    old
 * MemFree:          1436 kB    old
 * Buffers:          1312 kB    old
 * Cached:          20932 kB    old
 * Active:          12464 kB    new
 * Inact_dirty:      7772 kB    new
 * Inact_clean:      2008 kB    new
 * Inact_target:        0 kB    new
 * Inact_laundry:       0 kB    new, and might be missing too
 * HighTotal:           0 kB
 * HighFree:            0 kB
 * LowTotal:        61768 kB
 * LowFree:          1436 kB
 * SwapTotal:      122580 kB    old
 * SwapFree:        60352 kB    old
 * Inactive:        20420 kB    2.5.41+
 * Dirty:               0 kB    2.5.41+
 * Writeback:           0 kB    2.5.41+
 * Mapped:           9792 kB    2.5.41+
 * Shmem:              28 kB    2.6.32+
 * Slab:             4564 kB    2.5.41+
 * Committed_AS:     8440 kB    2.5.41+
 * PageTables:        304 kB    2.5.41+
 * ReverseMaps:      5738       2.5.41+
 * SwapCached:          0 kB    2.5.??+
 * HugePages_Total:   220       2.5.??+
 * HugePages_Free:    138       2.5.??+
 * Hugepagesize:     4096 kB    2.5.??+
 */

/* Shmem in 2.6.32+ */
unsigned long kb_main_shared;
/* old but still kicking -- the important stuff */
static unsigned long kb_page_cache;
unsigned long kb_main_buffers;
unsigned long kb_main_free;
unsigned long kb_main_total;
unsigned long kb_swap_free;
unsigned long kb_swap_total;
/* recently introduced */
unsigned long kb_high_free;
unsigned long kb_high_total;
unsigned long kb_low_free;
unsigned long kb_low_total;
unsigned long kb_main_available;
/* 2.4.xx era */
unsigned long kb_active;
unsigned long kb_inact_laundry;
unsigned long kb_inact_dirty;
unsigned long kb_inact_clean;
unsigned long kb_inact_target;
unsigned long kb_swap_cached; /* late 2.4 and 2.6+ only */
/* derived values */
unsigned long kb_main_cached;
unsigned long kb_swap_used;
unsigned long kb_main_used;
/* 2.5.41+ */
unsigned long kb_writeback;
unsigned long kb_slab;
unsigned long nr_reversemaps;
unsigned long kb_committed_as;
unsigned long kb_dirty;
unsigned long kb_inactive;
unsigned long kb_mapped;
unsigned long kb_pagetables;
// seen on a 2.6.x kernel:
static unsigned long kb_vmalloc_chunk;
static unsigned long kb_vmalloc_total;
static unsigned long kb_vmalloc_used;
// seen on 2.6.24-rc6-git12
static unsigned long kb_anon_pages;
static unsigned long kb_bounce;
static unsigned long kb_commit_limit;
static unsigned long kb_nfs_unstable;
// seen on 2.6.18
static unsigned long kb_min_free;
// 2.6.19+
static unsigned long kb_slab_reclaimable;
static unsigned long kb_slab_unreclaimable;
// 2.6.27+
static unsigned long kb_active_file;
static unsigned long kb_inactive_file;

#define MEMINFO_FILE "/proc/meminfo"
int mem_fd = -1;

#define BUFSIZE 2048
static char buf[BUFSIZE];

typedef struct mem_table_struct {
  const char *name;    /* memory type name */
  unsigned long *slot; /* slot in return struct */
} mem_table_struct;

static int compare_mem_table_structs(const void *a, const void *b) {
  return strcmp(((const mem_table_struct *)a)->name,
                ((const mem_table_struct *)b)->name);
}

void file2buf(char *filename) {
  int fd = -1;
  int local_n = -1;

  if ((fd = open(filename, O_RDONLY)) == -1) {
    perror("open file");
    _exit(EXIT_FAILURE);
  }

  lseek(fd, 0L, SEEK_SET);
  if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) {
    printf("hello world");
    perror(filename);
    _exit(EXIT_FAILURE);
  }

  buf[local_n] = '\0';

  // char name[16] = {0};
  // int slot = 0;
  // char *pbuf = buf;

  // size_t l = strlen("MemTotal:       16304740 kB");

  // while (sscanf(pbuf, "%s %d\n", name, &slot)) {
  //   printf("name=[%s] slot=[%d]\n", name, slot);
  //   char *end = strchr(pbuf, '\n');

  //   if (!end) break;
  //   pbuf = end + 1;
  // }
}

void meminfo(void) {
  char namebuf[16]; /* big enough to hold any row name */
  mem_table_struct findme = {namebuf, NULL};
  mem_table_struct *found;
  char *head;
  char *tail;
  static const mem_table_struct mem_table[] = {
      {"Active", &kb_active},  // important
      {"Active(file)", &kb_active_file},
      {"AnonPages", &kb_anon_pages},
      {"Bounce", &kb_bounce},
      {"Buffers", &kb_main_buffers},  // important
      {"Cached", &kb_page_cache},     // important
      {"CommitLimit", &kb_commit_limit},
      {"Committed_AS", &kb_committed_as},
      {"Dirty", &kb_dirty},  // kB version of vmstat nr_dirty
      {"HighFree", &kb_high_free},
      {"HighTotal", &kb_high_total},
      {"Inact_clean", &kb_inact_clean},
      {"Inact_dirty", &kb_inact_dirty},
      {"Inact_laundry", &kb_inact_laundry},
      {"Inact_target", &kb_inact_target},
      {"Inactive", &kb_inactive},  // important
      {"Inactive(file)", &kb_inactive_file},
      {"LowFree", &kb_low_free},
      {"LowTotal", &kb_low_total},
      {"Mapped", &kb_mapped},                // kB version of vmstat nr_mapped
      {"MemAvailable", &kb_main_available},  // important
      {"MemFree", &kb_main_free},            // important
      {"MemTotal", &kb_main_total},          // important
      {"NFS_Unstable", &kb_nfs_unstable},
      {"PageTables",
       &kb_pagetables},  // kB version of vmstat nr_page_table_pages
      {"ReverseMaps", &nr_reversemaps},  // same as vmstat nr_page_table_pages
      {"SReclaimable", &kb_slab_reclaimable},  // "slab reclaimable" (dentry and
                                               // inode structures)
      {"SUnreclaim", &kb_slab_unreclaimable},
      {"Shmem", &kb_main_shared},  // kernel 2.6.32 and later
      {"Slab", &kb_slab},          // kB version of vmstat nr_slab
      {"SwapCached", &kb_swap_cached},
      {"SwapFree", &kb_swap_free},    // important
      {"SwapTotal", &kb_swap_total},  // important
      {"VmallocChunk", &kb_vmalloc_chunk},
      {"VmallocTotal", &kb_vmalloc_total},
      {"VmallocUsed", &kb_vmalloc_used},
      {"Writeback", &kb_writeback},  // kB version of vmstat nr_writeback
  };
  const int mem_table_count = sizeof(mem_table) / sizeof(mem_table_struct);
  unsigned long watermark_low;
  signed long mem_available, mem_used;

  file2buf(MEMINFO_FILE);

  kb_inactive = ~0UL;
  kb_low_total = kb_main_available = 0;

  head = buf;
  for (;;) {
    tail = strchr(head, ':');
    if (!tail) break;
    *tail = '\0';
    if (strlen(head) >= sizeof(namebuf)) {
      head = tail + 1;
      goto nextline;
    }
    strcpy(namebuf, head);
    found = bsearch(&findme, mem_table, mem_table_count,
                    sizeof(mem_table_struct), compare_mem_table_structs);
    head = tail + 1;
    if (!found) goto nextline;
    *(found->slot) = (unsigned long)strtoull(head, &tail, 10);
  nextline:
    tail = strchr(head, '\n');
    if (!tail) break;
    head = tail + 1;
  }

  if (!kb_low_total) { /* low==main except with large-memory support */
    kb_low_total = kb_main_total;
    kb_low_free = kb_main_free;
  }

  if (kb_inactive == ~0UL) {
    kb_inactive = kb_inact_dirty + kb_inact_clean + kb_inact_laundry;
  }

  kb_main_cached = kb_page_cache + kb_slab_reclaimable;
  kb_swap_used = kb_swap_total - kb_swap_free;

  /* if kb_main_available is greater than kb_main_total or our calculation of
     mem_used overflows, that's symptomatic of running within a lxc container
     where such values will be dramatically distorted over those of the host. */
  if (kb_main_available > kb_main_total) kb_main_available = kb_main_free;
  mem_used = kb_main_total - kb_main_free - kb_main_cached - kb_main_buffers;
  if (mem_used < 0) mem_used = kb_main_total - kb_main_free;
  kb_main_used = (unsigned long)mem_used;

  /* zero? might need fallback for 2.6.27 <= kernel <? 3.14 */
  if (!kb_main_available) {
    kb_main_available = kb_main_free;
  }
}

#define S(X) (((unsigned long long)(X) << 10) >> byteshift)

static int byteshift = 10;
static int total = 0;

int main(int argc, char *argv[]) {
  int i;

  /* check startup flags */
  while ((i = getopt(argc, argv, "bkm")) != -1) switch (i) {
      case 'b':
        byteshift = 0;
        break;
      case 'k':
        byteshift = 10;
        break;
      case 'm':
        byteshift = 20;
        break;
      default:
        fprintf(stderr, "usage: %s [-b|-k|-m]\n", argv[0]);
        return 1;
    }

  meminfo();
    struct sysinfo s_info;
if(sysinfo(&s_info)==0)
{
  printf(
      "               total        used        free      shared  buff/cache  "
      " available");

  printf("\n");
  printf("%-9s", "Mem:");
  printf("%11lld", s_info.totalram/1024);
  printf(" %11lld", S(kb_main_used));
  printf(" %11lld", s_info.freeram/1024);
  printf(" %11lld", s_info.sharedram/1024);
  printf(" %11lld", S(kb_main_buffers + kb_main_cached));
  printf(" %11lld", S(kb_main_available));
  printf("\n");

  printf("%-9s", "Swap:");
  printf("%11lld", s_info.totalswap/1024);
  printf(" %11lld", S(kb_swap_used));
  printf(" %11lld", s_info.freeswap/1024);
  printf("\n");
}
  return 0;
}